package com.fingard.dsp.bank.directbank.weixin01;

import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.bankInterface.IEntrust;
import com.fingard.dsp.bank.reqrespobj.entrust.*;
import com.fingard.text.StringHelper;
import com.fingard.text.XmlHelper;
import com.fingard.util.SortHelper;
import com.fingard.xml.XmlTextReader;

import java.net.URLEncoder;
import java.util.ArrayList;

/**
 * 微信委托代扣纯签约
 */
public class WEIXIN01Entrust extends WEIXIN01Base implements IEntrust {
    /**
     * 参考文档：https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_1&index=1
     * <p>
     * 公众号纯签约：
     * 商户可以通过请求此接口唤起微信委托代扣的页面，页面样例见案例与规范。
     * 用户在微信的页面中完成代扣签约后，微信会同时将签约信息通过异步通知的方式通知给商户后台。如果用户放弃签约或签约失败则不通知。
     * <p>
     * 请求Url https://api.mch.weixin.qq.com/papay/entrustweb
     * 是否需要证书 否
     * 请求方式 GET
     * <p>
     * H5纯签约：
     * 该方式适用于手机、平板电脑等使用H5浏览器的设备场景使用微信签约扣款。商户在网站前端通过微信支付H5纯签约接口与用户签订委托扣款协议，再通过后台接口申请扣款来完成代扣交易。
     * 如果签约成功，微信会异步通知给商户（notify_url为通知路径），如果签约失败则不通知。
     * 特别提醒，H5纯签约接口为特权接口，需要单独申请权限，具体内容请咨询对接的业务人员
     * 请求Url https://api.mch.weixin.qq.com/papay/h5entrustweb
     * 是否需要证书 否
     * 请求方式 GET
     * 签名方式 HMAC-SHA256
     *
     * @param p_req
     * @return
     */
    @Override
    public RetAtsEntrust submitEntrust(ReqEntrust p_req) {
        RetAtsEntrust retAts = new RetAtsEntrust(p_req);

        try {
            ActSetItem actItem = getBankConfig().getActSetItem(p_req.ownAct, "unifiedorder");
            if (actItem != null) {
                ArrayList<String> arrayParams = new ArrayList<String>();
                ArrayList<String> arrayParam = new ArrayList<String>();
                arrayParams.add("appid=" + actItem.appId);
                arrayParam.add("appid=" + actItem.appId);
                arrayParams.add("mch_id=" + actItem.merchantId);
                arrayParam.add("mch_id=" + actItem.merchantId);

                if (StringHelper.hasAnyChar(p_req.planId)) {
                    String planId = XmlHelper.encode(p_req.planId);
                    arrayParams.add("plan_id=" + planId);
                    arrayParam.add("plan_id=" + planId);
                }

                if (StringHelper.hasAnyChar(p_req.authCode)) {
                    String authCode = XmlHelper.encode(p_req.authCode);
                    arrayParams.add("contract_code=" + authCode);
                    arrayParam.add("contract_code=" + authCode);
                }

                if (StringHelper.hasAnyChar(p_req.serialNo)) {
                    String serialNo = XmlHelper.encode(p_req.serialNo);
                    arrayParams.add("request_serial=" + serialNo);
                    arrayParam.add("request_serial=" + serialNo);
                }

                if (StringHelper.hasAnyChar(p_req.oppActName)) {
                    String oppActName = XmlHelper.encode(p_req.oppActName);
                    arrayParams.add("contract_display_account=" + oppActName);
                    arrayParam.add("contract_display_account=" + oppActName);
                }

                String notifyUrl = "";
                if ("01".equals(p_req.signType)) {
                    notifyUrl = actItem.getNotifyURLMap("entrustweb");
                    if (StringHelper.hasAnyChar(notifyUrl)) {
                        arrayParam.add("notify_url=" + notifyUrl);
                        arrayParams.add("notify_url=" + URLEncoder.encode(notifyUrl, getCharset()));
                    }
                } else if ("02".equals(p_req.signType)) {
                    notifyUrl = actItem.getNotifyURLMap("h5entrustweb");
                    if (StringHelper.hasAnyChar(notifyUrl)) {
                        arrayParam.add("notify_url=" + notifyUrl);
                        arrayParams.add("notify_url=" + URLEncoder.encode(notifyUrl, getCharset()));
                    }
                }

                arrayParams.add("version=1.0");
                arrayParam.add("version=1.0");
                String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
                if (StringHelper.hasAnyChar(timestamp)) {
                    arrayParams.add("timestamp=" + timestamp);
                    arrayParam.add("timestamp=" + timestamp);

                }

                if ("01".equals(p_req.signType)) {
                    String return_web = getBankConfig().getValueAsString("return_web");
                    if (StringHelper.hasAnyChar(return_web)) {
                        arrayParams.add("return_web=" + return_web);
                        arrayParam.add("return_web=" + return_web);
                    }
                }

                if ("02".equals(p_req.signType)) {
                    String createIp = getBankConfig().getValueAsString("createIP");
                    if (StringHelper.hasAnyChar(createIp)) {
                        arrayParams.add("clientip=" + createIp);
                        arrayParam.add("clientip=" + createIp);
                    }
                }

                String[] signParam = new String[arrayParam.size()];
                String[] signParams;
                arrayParam.toArray(signParam);
                String submitUrl = "";
                String sign = "";
                StringBuilder reqParams = new StringBuilder();
                if ("01".equals(p_req.signType)) {
                    sign = signMessageArray(signParam, actItem.saltKey, "MD5");
                    arrayParams.add("sign=" + sign);
                    signParams = new String[arrayParams.size()];
                    arrayParams.toArray(signParams);
                    String[] reqParam = SortHelper.BubbleSort(signParams);
                    for (int i = 0; i < reqParam.length; i++) {
                        if (i > 0) {
                            reqParams.append("&");
                        }
                        reqParams.append(reqParam[i]);
                    }
                    submitUrl = bankFront.getUrl("entrustweb") + "?" + reqParams;
                } else {
                    sign = signMessageArray(signParam, actItem.saltKey, "HMAC-SHA256");
                    arrayParams.add("sign=" + sign);
                    signParams = new String[arrayParams.size()];
                    arrayParams.toArray(signParams);
                    String[] reqParam = SortHelper.BubbleSort(signParams);
                    for (int i = 0; i < reqParam.length; i++) {
                        if (i > 0) {
                            reqParams.append("&");
                        }
                        reqParams.append(reqParam[i]);
                    }
                    submitUrl = bankFront.getUrl("h5entrustweb") + "?" + reqParams;
                }

                String[] tmpStrRet = new String[]{"", ""};
                if (submitUrl.contains("h5entrustweb")) {
                    tmpStrRet = sendToBank(reqParams.toString(), submitUrl);
                    if (tmpStrRet[0].length() == 0) {
                        StringBuilder tmpSbMsg = new StringBuilder();
                        XmlTextReader tmpBkReader = loadBankRespXmlReader(tmpStrRet[1], tmpSbMsg);
                        if (tmpBkReader != null) {
                            String return_code = tmpBkReader.getFirstText("/xml/return_code");
                            String return_msg = tmpBkReader.getFirstText("/xml/return_msg");

                            if (return_code != null && "SUCCESS".equals(return_code)) {
                                retAts.respCode = "0";
                                retAts.respInfo = tmpBkReader.getFirstText("/xml/return_msg");
                                if (StringHelper.hasAnyChar(tmpBkReader.getFirstText("/xml/err_code_des"))) {
                                    retAts.respInfo = tmpBkReader.getFirstText("/xml/err_code_des");
                                }
                                retAts.result = tmpBkReader.getFirstText("/xml/result_code");
                                retAts.entrustUrl = tmpBkReader.getFirstText("/xml/redirect_url");
                            } else {
                                retAts.respCode = "-1";
                                retAts.respInfo = return_msg;
                            }
                        } else {
                            retAts.respCode = "-1";
                            retAts.respInfo = tmpSbMsg.toString();
                        }
                    } else {
                        retAts.respCode = "-1";
                        retAts.respInfo = tmpStrRet[1];
                    }
                } else {
                    retAts.respCode = "0";
                    retAts.entrustUrl = submitUrl;
                }
            } else {
                retAts.respCode = "-1";
                retAts.respInfo = "未找到账号:[" + p_req.ownAct + "]商户配置信息,请检查'config/portfiles/" + p_req.reqHeader.bankCode + "/config.xml'中该账号相关商户配置节点是否配置正确!";
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            retAts.respCode = "-1";
            retAts.respInfo = ex.getMessage();
            WriteBankLogLn(ex);
        }

        return retAts;
    }

    /**
     * 参考文档：https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_4&index=9
     * <p>
     * 业务场景：
     * 商户与用户的签约关系有误或者商户主动要求与用户解除之前的签约协议时可调用此接口完成解约。目前商户侧支持两种模式的解约：
     * 1) contract_id模式：使用委托代扣协议id完成解约
     * 2)plan_id+contract_code模式：使用模板id和委托代扣协议号完成解约
     * 商户可以在商户后台（pay.weixin.qq.com）设置解约回调地址，当发生解约关系的时候，微信服务器会向此地址通知解约信息，内容与签约返回一致（详见签约返回）。
     * <p>
     * 请求Url https://api.mch.weixin.qq.com/papay/deletecontract
     * 是否需要证书 否
     * 请求方式 post
     *
     * @param p_req
     * @return
     */
    @Override
    public RetAtsDeEntrust submitDeEntrust(ReqDeEntrust p_req) {
        RetAtsDeEntrust retAts = new RetAtsDeEntrust(p_req);

        try {
            ActSetItem actItem = getBankConfig().getActSetItem(p_req.ownAct, "unifiedorder");
            if (actItem != null) {
                ArrayList<String> arrayParams = new ArrayList<String>();

                StringBuilder sbXml = new StringBuilder();
                sbXml.append("<xml>\r\n");

                sbXml.append("  <appid>" + actItem.appId + "</appid>\r\n");
                arrayParams.add("appid=" + actItem.appId);

                sbXml.append("  <mch_id>" + actItem.merchantId + "</mch_id>\r\n");
                arrayParams.add("mch_id=" + actItem.merchantId);

                if (StringHelper.hasAnyChar(p_req.channelAuthCode)) {
                    String channelAuthCode = XmlHelper.encode(p_req.channelAuthCode);
                    sbXml.append("  <contract_id>" + channelAuthCode + "</contract_id>\r\n");
                    arrayParams.add("contract_id=" + channelAuthCode);
                } else {
                    if (StringHelper.hasAnyChar(p_req.planId)) {
                        String planId = XmlHelper.encode(p_req.planId);
                        sbXml.append("  <plan_id>" + planId + "</plan_id>\r\n");
                        arrayParams.add("plan_id=" + planId);
                    }

                    if (StringHelper.hasAnyChar(p_req.authCode)) {
                        String authCode = XmlHelper.encode(p_req.authCode);
                        sbXml.append("  <contract_code>" + authCode + "</contract_code>\r\n");
                        arrayParams.add("contract_code=" + authCode);
                    }
                }

                if (StringHelper.hasAnyChar(p_req.memo)) {
                    String memo = XmlHelper.encode(p_req.memo);
                    sbXml.append("  <contract_termination_remark>" + memo + "</contract_termination_remark>\r\n");
                    arrayParams.add("contract_termination_remark=" + memo);
                }

                sbXml.append("  <version>1.0</version>\r\n");
                arrayParams.add("version=1.0");

                String[] signParams = new String[arrayParams.size()];
                arrayParams.toArray(signParams);
                String sign = signMessage(signParams, actItem.saltKey);
                sbXml.append("  <sign>" + sign + "</sign>\r\n");
                sbXml.append("</xml>");

                String[] tmpStrRet = sendToBank(sbXml.toString(), "deletecontract");

                if (tmpStrRet[0].length() == 0) {
                    StringBuilder tmpSbMsg = new StringBuilder();
                    XmlTextReader tmpBkReader = loadBankRespXmlReader(tmpStrRet[1], tmpSbMsg);
                    if (tmpBkReader != null) {
                        String return_code = tmpBkReader.getFirstText("/xml/return_code");
                        String return_msg = tmpBkReader.getFirstText("/xml/return_msg");
                        String result_code = tmpBkReader.getFirstText("/xml/result_code");

                        if (return_code != null && return_code.equals("SUCCESS")) {
                            if (result_code != null && result_code.equals("SUCCESS")) {
                                retAts.respCode = "0";
                                retAts.respInfo = return_msg;
                                retAts.planId = tmpBkReader.getFirstText("/xml/plan_id");
                                retAts.authCode = tmpBkReader.getFirstText("/xml/contract_code");
                                retAts.channelAuthCode = tmpBkReader.getFirstText("/xml/contract_id");
                            } else {
                                retAts.respCode = "-1";
                                retAts.respInfo = tmpBkReader.getFirstText("/xml/return_msg");
                                if (StringHelper.hasAnyChar(tmpBkReader.getFirstText("/xml/err_code_des"))) {
                                    retAts.respInfo = tmpBkReader.getFirstText("/xml/err_code_des");
                                }
                            }
                        } else {
                            retAts.respCode = "-1";
                            retAts.respInfo = return_msg;
                        }
                    } else {
                        retAts.respCode = "-1";
                        retAts.respInfo = tmpSbMsg.toString();
                    }
                } else {
                    retAts.respCode = "-1";
                    retAts.respInfo = tmpStrRet[1];
                }
            } else {
                retAts.respCode = "-1";
                retAts.respInfo = "未找到账号:[" + p_req.ownAct + "]商户配置信息,请检查'config/portfiles/" + p_req.reqHeader.bankCode + "/config.xml'中该账号相关商户配置节点是否配置正确!";
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            retAts.respCode = "-1";
            retAts.respInfo = ex.getMessage();
            WriteBankLogLn(ex);
        }

        return retAts;
    }

    /**
     * 参考文档：https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_2&index=7
     * <p>
     * 查询签约关系接口提供单笔签约关系查询：
     * 查询方式一：使用微信返回的委托代扣协议contract_id进行查询
     * 查询方式二：plan_id+contract_code模式：传入模板id和委托代扣协议号进行查询
     *
     * @param p_req
     * @return
     */
    @Override
    public RetAtsQueryEntrust queryEntrust(ReqQueryEntrust p_req) {
        RetAtsQueryEntrust retAts = new RetAtsQueryEntrust(p_req);

        try {
            ActSetItem actItem = getBankConfig().getActSetItem(p_req.ownAct, "unifiedorder");
            if (actItem != null) {
                ArrayList<String> arrayParams = new ArrayList<String>();

                StringBuilder sbXml = new StringBuilder();
                sbXml.append("<xml>\r\n");

                if (StringHelper.hasAnyChar(actItem.appId)) {
                    sbXml.append("  <appid>" + actItem.appId + "</appid>\r\n");
                    arrayParams.add("appid=" + actItem.appId);
                }

                if (StringHelper.hasAnyChar(actItem.merchantId)) {
                    sbXml.append("  <mch_id>" + actItem.merchantId + "</mch_id>\r\n");
                    arrayParams.add("mch_id=" + actItem.merchantId);
                }

                if (StringHelper.hasAnyChar(p_req.channelAuthCode)) {
                    String channelAuthCode = XmlHelper.encode(p_req.channelAuthCode);
                    sbXml.append("  <contract_id>" + channelAuthCode + "</contract_id>\r\n");
                    arrayParams.add("contract_id=" + channelAuthCode);
                } else {
                    if (StringHelper.hasAnyChar(p_req.planId)) {
                        String planId = XmlHelper.encode(p_req.planId);
                        sbXml.append("  <plan_id>" + planId + "</plan_id>\r\n");
                        arrayParams.add("plan_id=" + planId);
                    }

                    if (StringHelper.hasAnyChar(p_req.authCode)) {
                        String authCode = XmlHelper.encode(p_req.authCode);
                        sbXml.append("  <contract_code>" + authCode + "</contract_code>\r\n");
                        arrayParams.add("contract_code=" + authCode);
                    }
                }

                sbXml.append("  <version>1.0</version>\r\n");
                arrayParams.add("version=1.0");

                String[] signParams = new String[arrayParams.size()];
                arrayParams.toArray(signParams);
                String sign = signMessage(signParams, actItem.saltKey);
                sbXml.append("  <sign>" + sign + "</sign>\r\n");
                sbXml.append("</xml>");

                String[] tmpStrRet = sendToBank(sbXml.toString(), "querycontract");

                if (tmpStrRet[0].length() == 0) {
                    StringBuilder tmpSbMsg = new StringBuilder();
                    XmlTextReader tmpBkReader = loadBankRespXmlReader(tmpStrRet[1], tmpSbMsg);
                    if (tmpBkReader != null) {
                        String return_code = tmpBkReader.getFirstText("/xml/return_code");
                        String return_msg = tmpBkReader.getFirstText("/xml/return_msg");
                        String result_code = tmpBkReader.getFirstText("/xml/result_code");

                        if (return_code != null && return_code.equals("SUCCESS")) {
                            if (result_code != null && result_code.equals("SUCCESS")) {
                                retAts.respCode = "0";
                                retAts.respInfo = return_msg;
                                retAts.planId = tmpBkReader.getFirstText("/xml/plan_id");
                                retAts.authCode = tmpBkReader.getFirstText("/xml/contract_code");
                                retAts.channelAuthCode = tmpBkReader.getFirstText("/xml/contract_id");
                                retAts.timeEntrustStart = tmpBkReader.getFirstText("/xml/contract_signed_time");
                                retAts.timeEntrustExpire = tmpBkReader.getFirstText("/xml/contract_expired_time");
                                retAts.timeDeEntrustStart = tmpBkReader.getFirstText("/xml/contract_terminated_time");
                                retAts.deEntrustWay = tmpBkReader.getFirstText("/xml/contract_termination_mode");
                                retAts.entrustState = tmpBkReader.getFirstText("/xml/contract_state");
                                retAts.openId = tmpBkReader.getFirstText("/xml/openid");
                                retAts.appId = actItem.appId;
                            } else {
                                retAts.respCode = "-1";
                                retAts.respInfo = tmpBkReader.getFirstText("/xml/return_msg");
                                if (StringHelper.hasAnyChar(tmpBkReader.getFirstText("/xml/err_code_des"))) {
                                    retAts.respInfo = tmpBkReader.getFirstText("/xml/err_code_des");
                                }
                                String errCode = tmpBkReader.getFirstText("/xml/err_code");
                                if ("-25".equals(errCode)) {
                                    retAts.respCode = "0";
                                    retAts.entrustState = "S1";
                                }
                            }
                        } else {
                            retAts.respCode = "-1";
                            retAts.respInfo = return_msg;
                        }
                    } else {
                        retAts.respCode = "-1";
                        retAts.respInfo = tmpSbMsg.toString();
                    }
                } else {
                    retAts.respCode = "-1";
                    retAts.respInfo = tmpStrRet[1];
                }
            } else {
                retAts.respCode = "-1";
                retAts.respInfo = "未找到账号:[" + p_req.ownAct + "]商户配置信息,请检查'config/portfiles/" + p_req.reqHeader.bankCode + "/config.xml'中该账号相关商户配置节点是否配置正确!";
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            retAts.respCode = "-1";
            retAts.respInfo = ex.getMessage();
            WriteBankLogLn(ex);
        }

        return retAts;
    }
}